﻿using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using tenpayApp;
using Urs.Core;
using Urs.Data.Domain.Configuration;
using Urs.Data.Domain.Orders;
using Urs.Plugin.Payments.WeixinOpen.Models;
using Urs.Services.Configuration;
using Urs.Services.Localization;
using Urs.Services.Logging;
using Urs.Services.Orders;
using Urs.Services.Payments;
using Urs.Services.Security;
using Urs.Services.Stores;
using Urs.Framework.Controllers;

namespace Urs.Plugin.Payments.WeixinOpen.Controllers
{
    public class PaymentWeixinOpenController : BasePluginController
    {
        #region Fields

        private readonly ILocalizationService _localizationService;
        private readonly IPermissionService _permissionService;
        private readonly ISettingService _settingService;
        private readonly WeixinOpenPaymentSettings _weixinOpenPaymentSettings;
        private readonly PaymentSettings _paymentSettings;
        private readonly IPaymentService _paymentService;
        private readonly IOrderService _orderService;
        private readonly IOrderProcessingService _orderProcessingService;
        private readonly ILogger _logger;
        private readonly StoreInformationSettings _storeInformationSettings;
        private readonly IWebHelper _webHelper;
        private readonly IHttpContextAccessor _httpContextAccessor;

        #endregion

        #region Ctor

        public PaymentWeixinOpenController(ILocalizationService localizationService,
            IPermissionService permissionService,
            ISettingService settingService,
            WeixinOpenPaymentSettings weixinOpenPaymentSettings,
            PaymentSettings paymentSettings,
            IPaymentService paymentService,
            IOrderService orderService,
            IOrderProcessingService orderProcessingService, ILogger logger,
            StoreInformationSettings storeInformationSettings,
            IWebHelper webHelper,
            IHttpContextAccessor httpContextAccessor)
        {
            this._localizationService = localizationService;
            this._permissionService = permissionService;
            this._settingService = settingService;
            this._weixinOpenPaymentSettings = weixinOpenPaymentSettings;
            this._paymentSettings = paymentSettings;
            this._paymentService = paymentService;
            this._orderService = orderService;
            this._orderProcessingService = orderProcessingService;
            this._logger = logger;
            this._storeInformationSettings = storeInformationSettings;
            this._webHelper = webHelper;
            this._httpContextAccessor = httpContextAccessor;
        }

        #endregion

        #region Util
        public string getPostStr()
        {
            Int32 intLen = Convert.ToInt32(Request.Body.Length);
            byte[] b = new byte[intLen];
            Request.Body.Read(b, 0, intLen);
            return System.Text.Encoding.UTF8.GetString(b);
        }

        #endregion

        #region Methods

        [AdminAuthorize]
        [Area("Admin")]
        public IActionResult Configure()
        {
            //whether user has the authority
            if (!_permissionService.Authorize(StandardPermissionProvider.ManagePaymentMethods))
                return RedirectToAction("Unauthorized", "Security");

            var model = new ConfigurationModel();
            model.AppId = _weixinOpenPaymentSettings.AppId;
            model.AppSecret = _weixinOpenPaymentSettings.AppSecret;
            model.AppKey = _weixinOpenPaymentSettings.AppKey;
            model.Partner = _weixinOpenPaymentSettings.Partner;

            return View("~/Plugins/Payments.WeixinOpen/Views/Configure.cshtml", model);
        }

        [AdminAuthorize]
        [HttpPost, ActionName("Configure")]
        [Area("Admin")]
        public IActionResult Configure(ConfigurationModel model)
        {
            //whether user has the authority
            if (!_permissionService.Authorize(StandardPermissionProvider.ManagePaymentMethods))

                return RedirectToAction("Unauthorized", "Security");

            if (!ModelState.IsValid)
                return Json(new { error = 1 });

            //save settings
            _weixinOpenPaymentSettings.AppId = model.AppId;
            _weixinOpenPaymentSettings.AppSecret = model.AppSecret;
            _weixinOpenPaymentSettings.AppKey = model.AppKey;
            _weixinOpenPaymentSettings.Partner = model.Partner;
            _settingService.SaveSetting(_weixinOpenPaymentSettings);

            return Json(new { success = 1 });
        }

        public IActionResult PayingApi(int orderId, string paytype, string openId)
        {
            var processor = _paymentService.LoadPaymentMethodBySystemName(WeixinOpenPaymentDefaults.SystemName) as WeixinOpenPaymentMethod;
            if (processor == null ||
                !processor.IsPaymentMethodActive(_paymentSettings) || !processor.PluginDescriptor.Installed)
                throw new UrsException("微信支付[WeixinOpen]插件未加载");

            if (string.IsNullOrEmpty(openId))
                return Json(new { Code = 250, Content = "openId不能为空" });

            string out_trade_no = string.Empty; string total_fee = string.Empty;
            decimal orderPrice = decimal.Zero; DateTime createTime = DateTime.Parse("1900-01-01");
            if (paytype == nameof(Order) || paytype == nameof(Order).ToLower())
            {
                var order = _orderService.GetOrderById(orderId);
                if (order == null || order.OrderStatus != OrderStatus.Pending)
                    return Json(new { Code = 250, Content = "订单不存在或订单已支付" });

                if (order.PaymentMethodSystemName != WeixinOpenPaymentDefaults.SystemName)
                {
                    order.PaymentMethodSystemName = WeixinOpenPaymentDefaults.SystemName;
                    _orderService.UpdateOrder(order);
                }
                orderPrice = order.OrderTotal;
                createTime = order.CreateTime;
                total_fee = (order.OrderTotal * 100).ToString("0", CultureInfo.InvariantCulture);  //系统充值
                out_trade_no = string.Format("order-{0}-{1}", order.Id, new Random().Next(1000));
            }

            string nonceStr = TenpayUtil.getNoncestr();
            string notify_url = _webHelper.GetStoreLocation(false) + "Plugins/PaymentWeixinOpen/Return";
            string ip = _webHelper.GetCurrentIpAddress();
            var timeStart = DateTime.Now.ToString("yyyyMMddHHmmss");
            var timeExpire = DateTime.Now.AddMinutes(15).ToString("yyyyMMddHHmmss");
            var body = _storeInformationSettings.StoreName;
            var appId = _weixinOpenPaymentSettings.AppId;
            var appSecret = _weixinOpenPaymentSettings.AppSecret;
            var partner = _weixinOpenPaymentSettings.Partner;
            var appKey = _weixinOpenPaymentSettings.AppKey;
            var attach = body;
            var notifyUrl = _storeInformationSettings.StoreUrl + "Plugins/PaymentWeixinOpen/Notify";

            WxPayData data = new WxPayData(appKey);
            data.SetValue("body", body);
            data.SetValue("attach", body);
            data.SetValue("out_trade_no", out_trade_no);
            data.SetValue("total_fee", total_fee);
            data.SetValue("time_start", timeStart);
            data.SetValue("time_expire", timeExpire);
            data.SetValue("goods_tag", "SNSSHANHUSM");
            data.SetValue("trade_type", "JSAPI");
            data.SetValue("openid", openId);

            string initdataRequest;
            WxPayData result = WxPayApi.UnifiedOrder(data, appId, appKey, partner, notifyUrl, ip, out initdataRequest);

            if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
            {
                _logger.Error("微信支付统一下单出错：" + result.ToJson());
                return Json(new { Code = 250, Content = "提交给统一下单API的参数出错" });
            }
            if (paytype == nameof(Order) || paytype == nameof(Order).ToLower())
            {
                var order = _orderService.GetOrderById(orderId);
                if (order == null || order.OrderStatus != OrderStatus.Pending)
                    return Json(new { Code = 250, Content = "订单不存在或订单已支付" });
                order.Code = out_trade_no;
                order.PayInitalRequest = initdataRequest;
                _orderService.UpdateOrder(order);
            }
            WxPayData jsApiParam = new WxPayData(appKey);
            var appid = result.GetValue("appid");
            var timeStamp = WxPayApi.GenerateTimeStamp();
            var nonceStrValue = WxPayApi.GenerateNonceStr();
            var package = "prepay_id=" + result.GetValue("prepay_id");
            jsApiParam.SetValue("appId", appid);
            jsApiParam.SetValue("timeStamp", timeStamp);
            jsApiParam.SetValue("nonceStr", nonceStrValue);
            jsApiParam.SetValue("package", package);
            jsApiParam.SetValue("signType", "MD5");
            var paySign = jsApiParam.MakeSign();

            var mo = new MoPayment();
            mo.OrderId = orderId;
            mo.OrderPrice = PriceFormatter.FormatPrice(orderPrice);
            mo.CreateTime = createTime.ToString("yyyy-MM-dd HH:mm");
            foreach (var item in jsApiParam.ToDictionary())
                mo.KeyValue.Add(item.Key, item.Value.ToString());

            mo.KeyValue.Add("paySign", paySign);

            return Json(new { Code = 200, Content = "请求成功", data = mo });
        }

        public ActionResult Notify()
        {
            var processor = _paymentService.LoadPaymentMethodBySystemName("Payments.WeixinOpen") as WeixinOpenPaymentMethod;
            if (processor == null ||
                !processor.IsPaymentMethodActive(_paymentSettings) || !processor.PluginDescriptor.Installed)
                throw new UrsException("微信支付[WeixinOpen]插件未加载");

            string result = Processor(processor, RequestMethodType.POST);

            return Content(result);
        }

        public string Processor(WeixinOpenPaymentMethod processor, RequestMethodType methodType)
        {
            var appId = _weixinOpenPaymentSettings.AppId;
            var appSecret = _weixinOpenPaymentSettings.AppSecret;
            var partner = _weixinOpenPaymentSettings.Partner;
            var appKey = _weixinOpenPaymentSettings.AppKey;
            _logger.Information("支付回调中: appId:" + appId);
            var resultNotify = new ResultNotify(_httpContextAccessor);

            //接收从微信后台POST过来的数据
            var s = _httpContextAccessor.HttpContext.Request.Body;
            _logger.Information("回调Body:" + s);
            int count = 0;
            byte[] buffer = new byte[1024];
            StringBuilder builder = new StringBuilder();
            while ((count = s.Read(buffer, 0, 1024)) > 0)
            {
                builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
            }
            //s.Flush();
            s.Close();
            s.Dispose();

            _logger.Information("回调解析：xml" + builder.ToString());
            WxPayData notifyData = new WxPayData(appKey);
            try
            {
                notifyData.FromXml(builder.ToString());
            }
            catch (Exception ex)
            {
                //若签名错误，则立即返回结果给微信支付后台
                WxPayData res = new WxPayData(appKey);
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", ex.Message);
                return res.ToXml();
            }
            //检查支付结果中transaction_id是否存在
            if (!notifyData.IsSet("transaction_id"))
            {
                //若transaction_id不存在，则立即返回结果给微信支付后台
                WxPayData res = new WxPayData(appKey);
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", "支付结果中微信订单号不存在");
                return res.ToXml();
            }
            string transaction_id = notifyData.GetValue("transaction_id").ToString();
            string orderStr = notifyData.GetValue("out_trade_no").ToString();
            //查询订单，判断订单真实性
            if (!resultNotify.QueryOrder(transaction_id, appId, appKey, partner))
            {
                //若订单查询失败，则立即返回结果给微信支付后台
                WxPayData res = new WxPayData(appKey);
                res.SetValue("return_code", "FAIL");
                res.SetValue("return_msg", "订单查询失败");
                return res.ToXml();
            }
            //查询订单成功
            else
            {
                int orderId = 0;
                Match m = Regex.Match(orderStr, "(?<paytype>\\w+)-(?<orderId>\\w+)-\\d+");
                if (m.Success)
                {
                    var thirdNum = transaction_id;
                    var paytype = m.Groups["paytype"].Value;
                    var code = m.Groups["orderId"].Value;
                    if (Int32.TryParse(code, out orderId))
                    {
                        if ("Order" == paytype || "order" == paytype)
                        {
                            var order = _orderService.GetOrderById(orderId);
                            if (order != null && _orderProcessingService.CanMarkOrderAsPaid(order))
                            {
                                _orderProcessingService.MarkOrderAsPaid(order);
                                order.PayTransactionId = thirdNum;
                                order.PayInitalResponse = builder.ToString();
                                _orderService.UpdateOrder(order);
                            }
                        }
                    }
                }
                WxPayData res = new WxPayData(appKey);
                res.SetValue("return_code", "SUCCESS");
                res.SetValue("return_msg", "OK");
                return res.ToXml();
            }
        }
        #endregion
    }
    public enum RequestMethodType
    {
        GET = 0,
        POST = 1
    }
}